package org.hepeng.workx.web.filter;

import org.apache.commons.collections.CollectionUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;


/**
 * @author he peng
 */
public class FilterChainProxy extends OncePerRequestFilter {

    private List<Filter> filters;

    public FilterChainProxy(List<Filter> filters) {
        this.filters = filters;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        if (CollectionUtils.isEmpty(this.filters)) {
            filterChain.doFilter(request , response);
        } else {
            VirtualFilterChain virtualFilterChain = new VirtualFilterChain(filterChain, this.filters);
            virtualFilterChain.doFilter(request , response);
        }
    }

    private class VirtualFilterChain implements FilterChain {

        private final FilterChain originalChain;
        private List<Filter> filters;
        private final int size;
        private int position = 0;

        public VirtualFilterChain(FilterChain originalChain, List<Filter> filters) {
            this.originalChain = originalChain;
            this.filters = filters;
            this.size = filters.size();
        }

        @Override
        public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
            if (this.size == this.position) {
                this.originalChain.doFilter(request , response);
            } else {
                Filter nextFilter = nextFilter();
                nextFilter.doFilter(request , response , this);
            }
        }

        private Filter nextFilter() {
            int oldPosition = this.position;
            this.position++;
            return this.filters.get(oldPosition);
        }
    }
}
